﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Threading.Tasks;

namespace NETCommon.Domain
{
    public class NDomainLoader : MarshalByRefObject,IAssemblyLoader
    {
        private Assembly _assembly;
        private MethodInfo[] m_methods;
        private Type m_ExecutorType;
        private object m_Executor;
        private bool m_isLoad = false;

        public bool IsLoaded { get { return m_isLoad; } }
        public override object InitializeLifetimeService()
        {
            return null;
        }
        public void LoadAssembly(string FileName, params object[] args)
        {
            _assembly = Assembly.Load(AssemblyName.GetAssemblyName(FileName));
            m_ExecutorType = _assembly.GetTypes()[0];

            m_methods = m_ExecutorType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
            // sort methods by name
            Array.Sort(m_methods,
                    delegate(MethodInfo methodInfo1, MethodInfo methodInfo2)
                    { return methodInfo1.Name.CompareTo(methodInfo2.Name); });


            m_Executor = Activator.CreateInstance(m_ExecutorType, args);
            m_isLoad = true;
        }
        
        public void ExecuteAll(String strFilter)
        {
            foreach (var methodInfo in m_methods)
            {
                if (methodInfo.Name.Contains(strFilter))
                {
                    methodInfo.Invoke(m_Executor, null);
                }
            }
        }
        public void ExecuteAll(Func<MethodInfo, bool> Filter)
        {
            foreach (var methodInfo in m_methods)
            {
                if (Filter(methodInfo))
                {
                    methodInfo.Invoke(m_Executor, null);
                }
            }
        }
        public void ParallelExecuteAll(String strFilter)
        {
            Parallel.ForEach(m_methods, methodInfo =>
            {
                if (methodInfo.Name.Contains(strFilter))
                {
                    methodInfo.Invoke(m_Executor, null);
                }
            });
        }
        public void ParallelExecuteAll(Func<MethodInfo, bool> Filter)
        {
            Parallel.ForEach(m_methods, methodInfo =>
            {
                if (Filter(methodInfo))
                {
                    methodInfo.Invoke(m_Executor, null);
                }
            });
        }
        public Object Execute(string methodName, params object[] parameters)
        {
            MethodInfo method = m_ExecutorType.GetMethod(
                methodName,
                BindingFlags.Instance | BindingFlags.Public);
            return method.Invoke(null, parameters);
        }
        public object ExecuteStaticMethod(string typeName, string methodName, params object[] parameters)
        {
            Type type = _assembly.GetType(typeName);
            // TODO: this won't work if there are overloads available
            MethodInfo method = type.GetMethod(
                methodName,
                BindingFlags.Static | BindingFlags.Public);
            return method.Invoke(null, parameters);
        }
    }
}
